#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <list>


#include <misc.h>
#include "dfruntime.h"


// Simple circular buffer communication :

#define MSG_DATA_LEN 128
#define CB_LEN 128


struct msg {
    uint16_t type;
    char data[MSG_DATA_LEN];
};

struct circular_buffer {
       int head;
       int tail;
       struct msg buffer[CB_LEN];
};


void cb_init ( struct circular_buffer * cb ) {
    cb->head = 0;
    cb->tail = 0;
}


// Write with possible failure.
bool cb_write( struct circular_buffer * cb, const struct msg * msg ) {
    int head_next = (cb->head+1) % CB_LEN;
    if ( head_next == cb->tail ) {
        return false;
    }
    cb->buffer[cb->head] = *msg;
    cb->head = head_next;
    return true;
}

// Read : false when empty.
bool cb_read( struct circular_buffer * cb, struct msg *msg ) {
    if ( cb->head == cb->tail ) {
        // Buffer empty.
        return false;
    }
    // Copy message :
    *msg = cb->buffer[cb->tail];
    cb->tail = ( cb->tail + 1 ) % CB_LEN;
    return true;
}


// Global communication : CBSWITCH

static struct circular_buffer * cbswitch_p = NULL;
static int node_number;


#define SWACC(sender,receiver) (&cbswitch_p[(sender)*node_number + (receiver)])

void cbswitch_system_init( void *(*allocator)(size_t), int nodenum ) {

    cbswitch_p = (struct circular_buffer *) allocator( nodenum * nodenum * sizeof ( struct circular_buffer) ) ;
    for ( int i = 0; i < nodenum*nodenum; ++ i ) {
        cb_init( &cbswitch_p[i]);
    }
}


bool cbswitch_sendto(int from_id, int to_id, struct msg * in ) {
    ASSERT( from_id < node_number && from_id >= 0 );
    ASSERT( to_id < node_number && to_id >= 0 );
    return cb_write( SWACC(from_id,to_id), in );
}

bool cbswitch_readfrom(int from_id, int id, struct msg *out) {
    ASSERT( from_id < node_number && from_id >= 0 );
    ASSERT( id < node_number && id >= 0 );
    return cb_read( SWACC(from_id,id), out );

}

bool cbswitch_read( int id, struct msg * out ) {
    ASSERT( id < node_number && id >= 0 );
    int i = 0;
    while ( ! cb_read( SWACC(i,id), out) ) {
        i++;
        if ( i == node_number ) {
           return false;
        }
    }
    return true;
}



static __thread std::list<struct saved_msg> * to_send = NULL;

struct saved_msg {
    int to;
    struct msg msg;
};

void reliable_send_tls_init() {
    to_send = new std::list<struct saved_msg>();
}


void reliable_send( int from_id, int to_id,  struct msg *msg ) {
    int n_tosend = to_send->size();

    for (int i = 0; i < n_tosend; ++i ) {
        struct saved_msg sm = *to_send->begin();
        to_send->pop_front();
        if ( ! cbswitch_sendto(from_id, sm.to, &sm.msg ) ) {
            to_send->push_back(sm);
        }
    }

    if ( ! cbswitch_sendto(from_id, to_id, msg) ) {
        struct saved_msg sm;
        sm.to = to_id;
        sm.msg = * msg;
        to_send->push_back(sm);
    }
}

